/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.android.apps.mytracks.services.sensors;

import android.util.Log;

import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;

/**
 * An implementation of a SensorData parser for Polar Wearlink Bluetooth HRM.
 *
 * @author John R. Gerthoffer
 */
public class ZephyrMessageParser implements MessageParser {
  public int nLastHR = 0;
  
  @Override
  public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
    byte[] PolarBuffer = buffer.clone();
    StringBuilder sb = new StringBuilder();
    int nHR = 0;
//TODO    int nRR = 0;
    
    for (int i = 0; i < PolarBuffer.length; i++) {
      sb.append(String.format("%02X", PolarBuffer[i]));
    }
    Log.w(Constants.TAG, "Got Polar data: " + sb);

    // Due to an unknown issue, the packet does not always start at buffer[0].
    // While it works stepping thru in debug mode, it is not consistent when running real-time.
    // I am guessing it may be the re-alignment arraycopy call in BluetoothConnectionManager.

    // Minimum length Polar packets is 8, so stop search 8 bytes before buffer ends.
    nHR = nLastHR;                          // Default to use last value. (If our buffer is corrupted).
    for (int i = 0; i < PolarBuffer.length - 8; i++) {
      boolean bHdrOK = ((PolarBuffer[i] & 0xFF) == 254); 
      boolean bChkOK = ((PolarBuffer[i+2] & 0xFF) == (255 - (PolarBuffer[i+1] & 0xFF))); 
      boolean bSeqOK = ((PolarBuffer[i+3] & 0xFF) < 16); 
      boolean bStatusOK = ((PolarBuffer[i+4] & 0xFF) > 128);  // I've seen 0xF1 or 0xE1. 

      if (bHdrOK && bChkOK && bSeqOK && bStatusOK) {
        nHR = PolarBuffer[i + 5] & 0xFF;
        nLastHR = nHR;                      // Remember good value for next time.
//TODO          nRR = ((PolarBuffer[i + 6] & 0xFF) * 256) + (PolarBuffer[i + 7] & 0xFF);
        break;						        // Let's go store our data.  
      }
    }

//TODO Add RR Interval to BT sensor protocol and MyTracks data set.
//    // R to R Interval
//    Sensor.SensorData.Builder bRR = Sensor.SensorData.newBuilder()
//      .setValue(nRR)
//      .setState(Sensor.SensorState.SENDING);

    // Heart Rate
    Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder()
      .setValue(nHR)
      .setState(Sensor.SensorState.SENDING);

    Sensor.SensorDataSet sds =
      Sensor.SensorDataSet.newBuilder()
      .setCreationTime(System.currentTimeMillis())
      .setHeartRate(b)
//      .setRR(bRR)
      .build();
    
    return sds;
  }

  @Override
  public boolean isValid(byte[] buffer) {
	// Packet examples are;
	//   FE 08 F7 06 F1 48 03 64			Hdr Len Chk Seq Status HeartRate RRInterval_16-bits
	//   FE 0A F5 06 F1 48 03 64 03 70
	// where Hdr = 0xFE, Chk = 255 - Len
    return (((buffer[0] & 0xFF) == 254) && ((buffer[2] & 0xFF) == (255 - (buffer[1] & 0xFF))));
  }

  @Override
  public int getFrameSize() {
	// Polar uses variable packet sizes; 8, 10, 12, 14 and rarely 16.
    // The most frequent are 8 and 10.
	// We will wait for two packets of 10 bytes each.
    // This way, we are assured of getting one good one.
	return 16;									
  }

  @Override
  public int findNextAlignment(byte[] buffer) {
	try {
      // Minimum length Polar packets is 8, so stop search 8 bytes before buffer ends.
      for (int i = 0; i < buffer.length - 8; i++) {
        if (((buffer[i] & 0xFF) == 254) && ((buffer[i+2] & 0xFF) == (255 - (buffer[i+1] & 0xFF)))) {
	      return i;
        }
      }
	}
    catch (RuntimeException e) {
	      Log.w(Constants.TAG, "Polar alignment exception.", e);
	}

    return -1;
  }
}
